/*
 * BLLib.Configuration.OptionDescriptor
 * Andy Tidball
 * 
 * Project: Black Lightning General Library
 * Copyright: GNU General Public License
 */

using System;
using System.ComponentModel;

namespace BLLib.Configuration {
	internal class OptionDescriptor : PropertyDescriptor {
		// static data
		private static int  _CurrentValueIndex; // the option value index currently being manipulated by Get/SetValue
		private static bool _PropogateChanges;  // whether or not changes should propogate down the value type hierarchy

		/// <summary>
		/// Static constructor.
		/// </summary>
		static OptionDescriptor() {
			_CurrentValueIndex = 0;
			_PropogateChanges  = true;
		}

		/// <summary>
		/// Gets or sets the type of option value that will be manipulated by Get/SetValue.
		/// </summary>
		public static int CurrentValueIndex {
			get {
				return _CurrentValueIndex;
			}
			set {
				_CurrentValueIndex = value;
			}
		}

		/// <summary>
		/// Gets or sets whether or not value changes should propogate from high value indices to all lower ones.
		/// </summary>
		public static bool PropogateChanges {
			get {
				return _PropogateChanges;
			}
			set {
				_PropogateChanges = value;
			}
		}

		// member data
		private Option _Option; // the option that we're describing

		/// <summary>
		/// Creates a new OptionDescriptor.
		/// </summary>
		/// <param name="Option">The Option that this descriptor should describe.</param>
		public OptionDescriptor(Option Option)
			: base(Option.Name, Option.CreateAttributes()) {
			_Option = Option;
		}

		/// <summary>
		/// Gets the Option that this descriptor is describing.
		/// </summary>
		public Option Option {
			get {
				return _Option;
			}
		}

		/// <summary>
		/// Gets the type that this class describes.
		/// </summary>
		public override Type ComponentType {
			get {
				return typeof(Option);
			}
		}

		/// <summary>
		/// Gets whether or not the underlying option is read only.
		/// </summary>
		public override bool IsReadOnly {
			get {
				return _Option.ReadOnly;
			}
		}

		/// <summary>
		/// Gets the value type of the underlying option.
		/// </summary>
		public override Type PropertyType {
			get {
				return _Option.ValueType;
			}
		}

		/// <summary>
		/// Gets the value of the current option type from the underlying Option.
		/// </summary>
		/// <param name="Component">The Option to retrieve the value from (ignored, since it's stored internally).</param>
		/// <returns>The underlying Option's current value of the type OptionDescriptor.CurrentOptionValueType.</returns>
		public override object GetValue(object Component) {
			return _Option.GetTemporaryValue(CurrentValueIndex);
		}

		/// <summary>
		/// Sets the value of the current option type in the underlying Option.
		/// </summary>
		/// <param name="Component">The Option to set the value in (ignored, since it's stored internally).</param>
		/// <param name="Value">The value to store to the underlying Option's current value type.</param>
		public override void SetValue(object Component, object Value) {
			// make sure the given value's type works with the option
			if (Value.GetType() != _Option.ValueType) {
				throw new ArgumentException("Given value is of type '" + Value.GetType().Name + "' but the Option expects values of type '" + _Option.ValueType.Name + "'.");
			}

			// set the value
			_Option[CurrentValueIndex] = Value;

			// if the value needs to propogate, do that
			if (PropogateChanges) {
				// run from this index down to zero and store the same value
				for (int i = CurrentValueIndex; i >= 0; --i) {
					if (_Option.HasValueAtIndex(i)) {
						_Option[i] = Value;
					}
				}
			}
		}

		/// <summary>
		/// Resets the value of the underlying Option back to its permanent value.
		/// </summary>
		/// <param name="Component">The Option whose value to reset (ignored, since it's stored internally).</param>
		public override void ResetValue(object Component) {
			_Option.CancelNewValue(CurrentValueIndex);
		}

		/// <summary>
		/// Checks whether or not the underlying Option's value has changed.
		/// </summary>
		/// <param name="Component">The Option whose value to check (ignored, since it's stored internally).</param>
		/// <returns>True if the Object's current value has changed, false if it hasn't.</returns>
		public override bool CanResetValue(object Component) {
			return _Option.HasValueChanged(CurrentValueIndex);
		}

		/// <summary>
		/// Checks whether or not the underlying Option's value should be serialized (returns CanResetValue).
		/// </summary>
		/// <param name="Component">The Option whose value to check (ignored, since it's stored internally).</param>
		/// <returns>True if the Object's current value should be serialized (isn't the default), false if it shouldn't be (is the default).</returns>
		public override bool ShouldSerializeValue(object Component) {
			return CanResetValue(Component);
		}
	}
}
